define(function (require, exports, module) {
	var Swipeout = module.exports = function($el, config, view){
		this.initailize($el, config, view);
		return this;
	};
	
	/**
	 * @class Swipeout
	 * @memberof Nuui
	 * @classdesc 滑动删除(及其他操作)组件<br/>
	 * 			本组件实现了类似手机QQ聊天的滑动出现操作按钮的功能<br/>
	 * 			使用本组件时,需要注册一个区域,比如一个div,在该div范围内符合规范的元素(按照规范定义的li)都会被赋予滑动效果<br/>
	 * 			而且同一个区域,只需要注册一次,除非该区域被整体remove<br/>
	 * 			被注册的区域内任何时间存在的元素,无论是之前添加的还是之后添加的,都有效果<br/>
	 * 			
	 * 			
	 * @param {$} $el - 区域的$形式
	 * @param {object} config - 配置
	 * @param {boolean} config.swipeoutNoFollow - true,滑动一点就会滑出来,而且不能划出边界<br/>
	 * 											false,滑动更多才能划出来,而且可以划出边界<br/>
	 * 											默认为true
	 * @param {view} view - 当前的view,一定要填
	 * @example <div id="swpoutDemo" class="pt30 pb30">
	 * 	<h1 class="fc-red ft-32 pl30 pt15 pb15">静态示例</h1>
	 * 	<ul>
	 * 		<!-- 单个元素开始 -->
	 * 		<li class="nu-swipeout">
	 * 			<div class="nu-swipeout-content">
	 * 				向左滑动
	 * 			</div>
	 * 			<!-- 按钮分别写在nu-swipeout-actions-left或者nu-swipeout-actions-right里面,看需要 -->
	 * 			<div class="nu-swipeout-actions-right">
	 * 				<a class="nu-swipeout-red">删除</a>
	 * 			</div>
	 * 		</li>
	 * 		<!-- 单个元素结束 -->
	 * 		<li class="nu-swipeout">
	 * 			<div class="nu-swipeout-content">
	 * 				向左滑动
	 * 			</div>
	 * 			<div class="nu-swipeout-actions-right">
	 * 				<a class="nu-swipeout-gray">更多</a>
	 * 				<a class="nu-swipeout-orange">橙色</a>
	 * 				<a class="nu-swipeout-red">删除</a>
	 * 			</div>
	 * 		</li>
	 * 		<li class="nu-swipeout">
	 * 			<div class="nu-swipeout-content">
	 * 				向右滑动
	 * 			</div>
	 * 			<div class="nu-swipeout-actions-left">
	 * 				<a class="nu-swipeout-blue">蓝色</a>
	 * 				<a class="nu-swipeout-green">绿色</a>
	 * 			</div>
	 * 		</li>
	 * 		<li class="nu-swipeout">
	 * 			<div class="nu-swipeout-content">
	 * 				向左右滑动都可以
	 * 			</div>
	 * 			<div class="nu-swipeout-actions-left">
	 * 				<a class="nu-swipeout-blue">蓝色</a>
	 * 				<a class="nu-swipeout-green">绿色</a>
	 * 			</div>
	 * 			<div class="nu-swipeout-actions-right">
	 * 				<a class="nu-swipeout-gray">更多</a>
	 * 				<a class="nu-swipeout-orange">橙色</a>
	 * 				<a class="nu-swipeout-red">删除</a>
	 * 			</div>
	 * 		</li>
	 * 	</ul>
	 * </div>
	 * var swipeoutDemo = new Swipeout(view.$("#swpoutDemo"), {}, view); // 注册
	 */
	Swipeout.prototype = {
		/**
		 * 初始化方法
		 */
		initailize:function($el, config, view){
			this._config = $.extend({}, this._defaultConfig);
			if(config){
				this._config = $.extend(this._config, config);
			}
			
			// 由于目前采取单页模式,创建时必须输入view参数
			if(!view){
				alert("swipeout:请输入view参数");
				return;
			}
			
			this._doc = view ? view.$el : document.body;
			
			this._$el = $el;
			
			this._addFuncTo$();
			
			this._render();
			
		},
		
		/**
		 * 默认设置
		 */
		_defaultConfig:{
			swipeoutNoFollow:true
		},
		/**
		 * 点击事件
		 */
		_tap_event:"tap",
		
		_begin_event:("ontouchstart" in document) ? "touchstart" : "mousedown",
				
		_move_event:("ontouchmove" in document) ? "touchmove" : "mousemove",
				
		_end_event:("ontouchend" in document) ? "touchend" : "mouseup",
		
		/**
		 * 作用域
		 */
		_doc:null,
		
		_swipeoutOpenedEl:null,
		
		_isTouched:null,
		
		_isMoved:null,
		
		_isScrolling:null,
		
		_touchesStartX:null,
		
		_touchesStartY:null,
		
		_touchStartTime:null,
		
		_touchesDiff:null,
		
		_swipeOutEl:null,

		_swipeOutContent:null,

		_actionsRight:null,

		_actionsLeft:null,

		_actionsLeftWidth:null,

		_actionsRightWidth:null,

		_translate:null,

		_opened:null,

		_openedActions:null,

		_buttonsLeft:null,

		_buttonsRight:null,
		
		_direction:null,
		
		_render:function(){
			
			// 监听关闭所有
			$(this._doc).on(this._begin_event, $.proxy(function(e){
				e = e.originalEvent || e;
				if(this._swipeoutOpenedEl){
					var target = $(e.target);
					if(!(this._swipeoutOpenedEl.is(target[0]) || target.parents('.nu-swipeout').is(this._swipeoutOpenedEl))){
						this._swipeoutClose(this._swipeoutOpenedEl);
					}
				}
			}, this));

			
			this._$el.on(this._begin_event, 'li.nu-swipeout', $.proxy(this._handleTouchStart, this));
			this._$el.on(this._move_event, 'li.nu-swipeout', $.proxy(this._handleTouchMove, this));
			this._$el.on(this._end_event, 'li.nu-swipeout', $.proxy(this._handleTouchEnd, this));
			
		},
		
		_handleTouchStart:function(e){
			e = e.originalEvent || e;
			this._isMoved = false;
			this._isTouched = true;
			this._isScrolling = undefined;
			this._touchesStartX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
			this._touchesStartY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
			this._touchStartTime = (new Date()).getTime();
		},
		
		_handleTouchMove:function(e){
			var currentTarget = e.currentTarget; // for jq
			e = e.originalEvent || e;
			if(!this._isTouched){
				return;
			}
			var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
			var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
			if(typeof this._isScrolling === 'undefined'){
				this._isScrolling = !!(this._isScrolling || Math.abs(pageY - this._touchesStartY) > Math.abs(pageX - this._touchesStartX));
			}
			if(this._isScrolling){
				this._isTouched = false;
				return;
			}

			if(!this._isMoved){
				
				/*jshint validthis:true */
				this._swipeOutEl = $(currentTarget);
				this._swipeOutContent = this._swipeOutEl.find('.nu-swipeout-content');
				this._actionsRight = this._swipeOutEl.find('.nu-swipeout-actions-right');
				this._actionsLeft = this._swipeOutEl.find('.nu-swipeout-actions-left');
				this._actionsLeftWidth = this._actionsRightWidth = this._buttonsLeft = this._buttonsRight = null;
				if(this._actionsLeft.length > 0){
					this._actionsLeftWidth = this._actionsLeft.outerWidth();
					this._buttonsLeft = this._actionsLeft.children('a');
				}
				if(this._actionsRight.length > 0){
					this._actionsRightWidth = this._actionsRight.outerWidth();
					this._buttonsRight = this._actionsRight.children('a');
				}
				this._opened = this._swipeOutEl.hasClass('nu-swipeout-opened');
				if(this._opened){
					this._openedActions = this._swipeOutEl.find('.nu-swipeout-actions-left.nu-swipeout-actions-opened').length > 0 ? 'left' : 'right';
				}
				this._swipeOutEl.removeClass('nu-transitioning');
				if(!this._config.swipeoutNoFollow){
					this._swipeOutEl.find('.nu-swipeout-actions-opened').removeClass('nu-swipeout-actions-opened');
					this._swipeOutEl.removeClass('nu-swipeout-opened');
				}
			}
			this._isMoved = true;
			e.preventDefault();

			this._touchesDiff = pageX - this._touchesStartX;
			this._translate = this._touchesDiff;

			if(this._opened){
				if(this._openedActions === 'right')
					this._translate = this._translate - this._actionsRightWidth;
				else
					this._translate = this._translate + this._actionsLeftWidth;
			}

			if(this._translate > 0 && this._actionsLeft.length === 0 || this._translate < 0 && this._actionsRight.length === 0){
				if(!this._opened){
					this._isTouched = this._isMoved = false;
					this._swipeOutContent.transform('');
					if(this._buttonsRight && this._buttonsRight.length > 0){
						this._buttonsRight.transform('');
					}
					if(this._buttonsLeft && this._buttonsLeft.length > 0){
						this._buttonsLeft.transform('');
					}
					return;
				}
				this._translate = 0;
			}

			if(this._translate < 0){
				this._direction = 'to-left';
			}else if(this._translate > 0){
				this._direction = 'to-right';	
			}else{
				if(this._direction){
					this._direction = this._direction;
				}else{
					this._direction = 'to-left';
				}
			}

			var i, buttonOffset, progress;

			e.f7PreventPanelSwipe = true;
			if(this._config.swipeoutNoFollow){
				if(this._opened){
					if(this._openedActions === 'right' && this._touchesDiff > 0){
						this._swipeoutClose(this._swipeOutEl);
					}
					if(this._openedActions === 'left' && this._touchesDiff < 0){
						this._swipeoutClose(this._swipeOutEl);
					}
				}else{
					if(this._touchesDiff < 0 && this._actionsRight.length > 0){
						this._swipeoutOpen(this._swipeOutEl, 'right');
					}
					if(this._touchesDiff > 0 && this._actionsLeft.length > 0){
						this._swipeoutOpen(this._swipeOutEl, 'left');
					}
				}
				this._isTouched = false;
				this._isMoved = false;
				return;
			}
			var $button;
			if(this._actionsRight.length > 0){
				// Show right actions
				progress = this._translate / this._actionsRightWidth;
				if(this._translate < -this._actionsRightWidth){
					this._translate = -this._actionsRightWidth - Math.pow(-this._translate - this._actionsRightWidth, 0.8);
				}
				for(i = 0; i < this._buttonsRight.length; i++){
					if(typeof this._buttonsRight[i]._buttonOffset === 'undefined'){
						this._buttonsRight[i]._buttonOffset = this._buttonsRight[i].offsetLeft;
					}
					buttonOffset = this._buttonsRight[i]._buttonOffset;
					$button = $(this._buttonsRight[i]);
					
					$button.transform('translate3d(' + (this._translate - buttonOffset * (1 + Math.max(progress, -1))) + 'px,0,0)');
				}
			}
			if(this._actionsLeft.length > 0){
				// Show left actions
				progress = this._translate / this._actionsLeftWidth;
				if(this._translate > this._actionsLeftWidth){
					this._translate = this._actionsLeftWidth + Math.pow(this._translate - this._actionsLeftWidth, 0.8);
				}
				for(i = 0; i < this._buttonsLeft.length; i++){
					if(typeof this._buttonsLeft[i]._buttonOffset === 'undefined'){
						this._buttonsLeft[i]._buttonOffset = this._actionsLeftWidth - this._buttonsLeft[i].offsetLeft - this._buttonsLeft[i].offsetWidth;
					}
					buttonOffset = this._buttonsLeft[i]._buttonOffset;
					$button = $(this._buttonsLeft[i]);
					
					if(this._buttonsLeft.length > 1){
						$button.css('z-index', this._buttonsLeft.length - i);
					}
					$button.transform('translate3d(' + (this._translate + buttonOffset * (1 - Math.min(progress, 1))) + 'px,0,0)');
				}
			}
			this._swipeOutContent.transform('translate3d(' + this._translate + 'px,0,0)');
		},
		
		_handleTouchEnd:function(e){
			e = e.originalEvent || e;
			if(!this._isTouched || !this._isMoved){
				this._isTouched = false;
				this._isMoved = false;
				return;
			}

			this._isTouched = false;
			this._isMoved = false;
			var timeDiff = (new Date()).getTime() - this._touchStartTime;
			var action, actionsWidth, actions, buttons, i;

			actions = this._direction === 'to-left' ? this._actionsRight : this._actionsLeft;
			actionsWidth = this._direction === 'to-left' ? this._actionsRightWidth : this._actionsLeftWidth;

			if(timeDiff < 300 && (this._touchesDiff < -10 && this._direction === 'to-left' || this._touchesDiff > 10 && this._direction === 'to-right') || timeDiff >= 300 && Math.abs(this._translate) > actionsWidth / 2){
				action = 'open';
			}else{
				action = 'close';
			}
			if(timeDiff < 300){
				if(Math.abs(this._translate) === 0){
					action = 'close';
				}
				if(Math.abs(this._translate) === actionsWidth){
					action = 'open';
				}
			}

			if(action === 'open'){
				this._swipeoutOpenedEl = this._swipeOutEl;
				this._swipeOutEl.trigger('open');
				this._swipeOutEl.addClass('nu-swipeout-opened nu-transitioning');
				var newTranslate = this._direction === 'to-left' ? -actionsWidth : actionsWidth;
				this._swipeOutContent.transform('translate3d(' + newTranslate + 'px,0,0)');
				actions.addClass('nu-swipeout-actions-opened');
				buttons = this._direction === 'to-left' ? this._buttonsRight : this._buttonsLeft;
				if(buttons){
					for(i = 0; i < buttons.length; i++){
						$(buttons[i]).transform('translate3d(' + newTranslate + 'px,0,0)');
					}
				}
			}else{
				this._swipeOutEl.trigger('close');
				this._swipeoutOpenedEl = undefined;
				this._swipeOutEl.addClass('nu-transitioning').removeClass('nu-swipeout-opened');
				this._swipeOutContent.transform('');
				actions.removeClass('nu-swipeout-actions-opened');
			}

			var buttonOffset;
			if(this._buttonsLeft && this._buttonsLeft.length > 0 && this._buttonsLeft !== buttons){
				for(i = 0; i < this._buttonsLeft.length; i++){
					buttonOffset = this._buttonsLeft[i]._buttonOffset;
					if(typeof buttonOffset === 'undefined'){
						this._buttonsLeft[i]._buttonOffset = this._actionsLeftWidth - this._buttonsLeft[i].offsetLeft - this._buttonsLeft[i].offsetWidth;
					}
					$(this._buttonsLeft[i]).transform('translate3d(' + (buttonOffset) + 'px,0,0)');
				}
			}
			if(this._buttonsRight && this._buttonsRight.length > 0 && this._buttonsRight !== buttons){
				for(i = 0; i < this._buttonsRight.length; i++){
					buttonOffset = this._buttonsRight[i]._buttonOffset;
					if(typeof buttonOffset === 'undefined'){
						this._buttonsRight[i]._buttonOffset = this._buttonsRight[i].offsetLeft;
					}
					$(this._buttonsRight[i]).transform('translate3d(' + (-buttonOffset) + 'px,0,0)');
				}
			}
			this._swipeOutContent.transitionEnd($.proxy(function(e){
				if(this._opened && action === 'open' || closed && action === 'close'){
					return;
				}
				this._swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed');
				if(this._opened && action === 'close'){
					if(this._actionsRight.length > 0){
						this._buttonsRight.transform('');
					}
					if(this._actionsLeft.length > 0){
						this._buttonsLeft.transform('');
					}
				}
			}, this));
		},
		
		_swipeoutOpen:function(el, dir, callback){
			el = $(el);
			if(arguments.length === 2){
				if(typeof arguments[1] === 'function'){
					callback = dir;
				}
			}

			if(el.length === 0)
				return;
			if(el.length > 1)
				el = $(el[0]);
			if(!el.hasClass('nu-swipeout') || el.hasClass('nu-swipeout-opened'))
				return;
			if(!dir){
				if(el.find('.nu-swipeout-actions-right').length > 0)
					dir = 'right';
				else
					dir = 'left';
			}
			var swipeOutActions = el.find('.nu-swipeout-actions-' + dir);
			if(swipeOutActions.length === 0)
				return;
			el.trigger('open').addClass('nu-swipeout-opened').removeClass('nu-transitioning');
			swipeOutActions.addClass('nu-swipeout-actions-opened');
			var buttons = swipeOutActions.children('a');
			var swipeOutActionsWidth = swipeOutActions.outerWidth();
			var translate = dir === 'right' ? -swipeOutActionsWidth : swipeOutActionsWidth;
			var i;
			if(buttons.length > 1){
				for(i = 0; i < buttons.length; i++){
					if(dir === 'right'){
						$(buttons[i]).transform('translate3d(' + (-buttons[i].offsetLeft) + 'px,0,0)');
					}else{
						$(buttons[i]).css('z-index', buttons.length - i).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
					}
				}
				var clientLeft = buttons[1].clientLeft;
			}
			el.addClass('nu-transitioning');
			for(i = 0; i < buttons.length; i++){
				$(buttons[i]).transform('translate3d(' + (translate) + 'px,0,0)');
			}
			el.find('.nu-swipeout-content').transform('translate3d(' + translate + 'px,0,0)').transitionEnd($.proxy(function(){
				el.trigger('opened');
				if(callback)
					callback.call(el[0]);
			}, this));
			this._swipeoutOpenedEl = el;
		},
		
		_swipeoutClose:function(el, callback){
			el = $(el);
			if(el.length === 0)
				return;
			if(!el.hasClass('nu-swipeout-opened'))
				return;
			var dir = el.find('.nu-swipeout-actions-opened').hasClass('nu-swipeout-actions-right') ? 'right' : 'left';
			var swipeOutActions = el.find('.nu-swipeout-actions-opened').removeClass('nu-swipeout-actions-opened');
			var buttons = swipeOutActions.children('a');
			var swipeOutActionsWidth = swipeOutActions.outerWidth();
			this._allowSwipeout = false;
			el.trigger('close');
			el.removeClass('nu-swipeout-opened').addClass('nu-transitioning');

			var closeTO;
			var that = this;
			function onSwipeoutClose(){
				that._allowSwipeout = true;
				if(el.hasClass('nu-swipeout-opened'))
					return;
				el.removeClass('nu-transitioning');
				buttons.transform('');
				el.trigger('closed');
				if(callback)
					callback.call(el[0]);
				if(closeTO)
					clearTimeout(closeTO);
			}
			el.find('.nu-swipeout-content').transform('').transitionEnd(onSwipeoutClose);
			closeTO = setTimeout(onSwipeoutClose, 500);

			for(var i = 0; i < buttons.length; i++){
				if(dir === 'right'){
					$(buttons[i]).transform('translate3d(' + (-buttons[i].offsetLeft) + 'px,0,0)');
				}else{
					$(buttons[i]).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
				}
				$(buttons[i]).css({
					left:0 + 'px'
				});
			}
			if(this._swipeoutOpenedEl && this._swipeoutOpenedEl[0] === el[0])
				this._swipeoutOpenedEl = undefined;
		},
		
		_swipeoutDelete:function(el, callback){
			el = $(el);
			if(el.length === 0)
				return;
			if(el.length > 1)
				el = $(el[0]);
			this._swipeoutOpenedEl = undefined;
			el.trigger('delete');
			el.css({
				height:el.outerHeight() + 'px'
			});
			var clientLeft = el[0].clientLeft;
			el.css({
				height:0 + 'px'
			}).addClass('nu-deleting nu-transitioning').transitionEnd($.proxy(function(){
				el.trigger('deleted');
				if(callback){
					callback.call(el[0]);
				}
				el.remove();
			}, this));
			var translate = '-100%';
			el.find('.nu-swipeout-content').transform('translate3d(' + translate + ',0,0)');
		},
		
		_addFuncTo$:function(){
			$.fn.transform = $.fn.transform || function(transform){
				for(var i = 0; i < this.length; i++){
					var elStyle = this[i].style;
					elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
				}
				return this;
			};
			
			$.fn.transitionEnd = $.fn.transitionEnd || function(callback){
				var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'], i, j, dom = this;
				function fireCallBack(e){
					/* jshint validthis:true */
					if(e.target !== this)
						return;
					callback.call(this, e);
					for(i = 0; i < events.length; i++){
						dom.off(events[i], fireCallBack);
					}
				}
				if(callback){
					for(i = 0; i < events.length; i++){
						dom.on(events[i], fireCallBack);
					}
				}
				return this;
			}
		},
		
		/**
		 * @desc 手工滑动打开元素,带动画
		 * @memberof Nuui.Swipeout
		 * @func openItem
		 * @instance
		 * @param {$el} 要打开的元素的$形式
		 */
		openItem:function($el){
			this._swipeoutOpen($el);
		},
		
		/**
		 * @desc 手工滑动关闭元素,带动画
		 * @memberof Nuui.Swipeout
		 * @func closeItem
		 * @instance
		 * @param {$el} 要关闭的元素的$形式
		 */
		closeItem:function($el){
			this._swipeoutClose($el);
		},
		
		/**
		 * @desc 手工删除元素,带动画
		 * @memberof Nuui.Swipeout
		 * @func deleteItem
		 * @instance
		 * @param {$el} 要删除的元素的$形式
		 */
		deleteItem:function($el){
			this._swipeoutDelete($el);
		}
	};
});